/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.google.code.twiddling.core.util;

import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

import com.google.code.twiddling.core.clp.Argument;
import com.google.code.twiddling.core.clp.Option;
import com.google.code.twiddling.core.command.Command;
import com.google.code.twiddling.core.command.ICommand;
import com.google.code.twiddling.core.io.text.TextObjectFactory;
import com.google.code.twiddling.core.io.text.TextTable;
import com.google.code.twiddling.core.layout.CommandHelpLayout;
import com.google.code.twiddling.core.layout.LayoutManager;

/**
 * @author <a href="mailto:jeff.yuchang@gmail.com">Jeff Yu</a>
 *
 */
public class DocUtil {
	
	public static final String END_OF_LINE = "\n";
	
	public static DocModel getDocModel(Class<? extends ICommand> command) {
		return new DocModel(command);
	}
	
	/**
	 * Render the usage, options, arguments from its annotation into docbook format,
	 * so that can be reused into the user guide.
	 * @param command
	 * @return
	 */
	public static String renderToDocBook(Class<? extends ICommand> command) {
		
		DocModel doc= getDocModel(command);
		Command cmd = doc.getCommand();		
		
		StringBuffer buffer = new StringBuffer();
		buffer.append("<section id = \"" + cmd.name() + "\">" + END_OF_LINE);
		buffer.append("<title> " + cmd.name() + " </title>" + END_OF_LINE);
		if (!cmd.usage().equals("")) {
			buffer.append("<para> Usage : " + cmd.usage() + "</para>" + END_OF_LINE);
		}
		if (!cmd.description().equals("")) {
			buffer.append("<para> Description : " + cmd.description() + "</para>" + END_OF_LINE);
		}
		if (!cmd.example().equals("")) {
			buffer.append("<para> Example : " + cmd.example() + "</para>" + END_OF_LINE);
		}
		
		if (doc.getOptions().size() > 0) {
			buffer.append("<table> \n");
			buffer.append(" <title> options </title> \n");
			buffer.append("  <tgroup cols=\"3\">\n");
			buffer.append("  <thead>\n");
			buffer.append("    <row>\n");
			buffer.append("      <entry> option </entry>\n");
			buffer.append("      <entry> alias </entry>\n");
			buffer.append("      <entry> description </entry>\n");
			buffer.append("     </row>\n");
			buffer.append("   </thead>\n");
			
			buffer.append("   <tbody>\n");
			for (Option option : doc.getOptions()) {
				buffer.append("   <row>" + END_OF_LINE);
				buffer.append("      <entry>" + option.name() + "</entry>" + END_OF_LINE);
				buffer.append("      <entry>" + StringUtil.getArrayString(option.aliases(), ",") + "</entry>" + END_OF_LINE);
				buffer.append("      <entry>" + option.description() + "</entry>" + END_OF_LINE);
				buffer.append("    </row>" + END_OF_LINE);
			}
			buffer.append("    </tbody>\n");
			buffer.append("   </tgroup>\n" );
			buffer.append("</table>\n");
		}
		
		buffer.append("</section>");
		
		return buffer.toString();
	}
	
	/**
	 * Render the usage, options and arguments to console.
	 * @param command
	 * @param out
	 */
	public static void renderToConsole(Class<? extends ICommand> command, PrintWriter out) {
		DocModel doc= getDocModel(command);
		Command cmd = doc.getCommand();	
		
		CommandHelpLayout layout = LayoutManager.getHelpLayout();
		
		layout.setUsage(cmd.usage())
		.setDescription(cmd.description())
		.setExample(cmd.example());
		
		List<Option> options = doc.getOptions();
		
		for (Option option : options) {
			layout.addOption(option.name(), StringUtil.getArrayString(option.aliases(), ","), option.description());
		}
		
		layout.print(out);
	}
	
	public static void println(PrintWriter out, String value, String title) {
		if (!StringUtil.isEmpty(value)) {
			if (!StringUtil.isEmpty(title)) {
				out.println(title + " : " + value);
			} else {
				out.println(value);
			}
		}
	}
	
	public static class DocModel {
		private Command command;
		private List<Option> options = new LinkedList<Option>();
		private List<Argument> arguments = new LinkedList<Argument>();
		
		public DocModel(Class<? extends ICommand> command) {
			
			this.command = command.getAnnotation(Command.class);
			
	        for (Class<?> type=command; type!=null; type=type.getSuperclass()) {
	            // Discover methods
	            for (Method method : type.getDeclaredMethods()) {
	                Option option = method.getAnnotation(Option.class);
	                if (option != null) {
	                    options.add(option);
	                }

	                Argument argument = method.getAnnotation(Argument.class);
	                if (argument != null) {
	                    arguments.add(argument);
	                }
	            }

	            // Discover fields
	            for (Field field : type.getDeclaredFields()) {
	                Option option = field.getAnnotation(Option.class);
	                if (option != null) {
	                    options.add(option);
	                }

	                Argument argument = field.getAnnotation(Argument.class);
	                if (argument != null) {
	                    arguments.add(argument);
	                }
	            }
	        }
		}

		public Command getCommand() {
			return command;
		}

		public List<Option> getOptions() {
			return options;
		}

		public List<Argument> getArguments() {
			return arguments;
		}
			
	}

}
